package cn.iocoder.yudao.module.system.service.examscore;

import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
import cn.iocoder.yudao.module.system.controller.admin.examscore.vo.ExamScoreImportRespVO;
import cn.iocoder.yudao.module.system.controller.admin.examscore.vo.NewExamScoreImportExcelVO;
import cn.iocoder.yudao.module.system.dal.dataobject.examclass.ExamClassDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examclass.ExamClassStudentDO;
import cn.iocoder.yudao.module.system.dal.dataobject.examscore.ExamScoreDO;
import cn.iocoder.yudao.module.system.dal.dataobject.subjectscoredetail.SubjectScoreDetailDO;
import cn.iocoder.yudao.module.system.dal.dataobject.topscorerecord.TopScoreRecordDO;
import cn.iocoder.yudao.module.system.dal.mysql.examclass.ExamClassMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examclass.ExamClassStudentMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examscore.ExamScoreMapper;
import cn.iocoder.yudao.module.system.dal.mysql.examscore.ExamScoreMapperBatch;
import cn.iocoder.yudao.module.system.dal.mysql.subjectscoredetail.SubjectScoreDetailMapper;
import cn.iocoder.yudao.module.system.dal.mysql.topscorerecord.TopScoreRecordMapper;
import cn.iocoder.yudao.module.system.service.autogeneration.AutoGenerationService;
import cn.iocoder.yudao.module.system.service.ranking.RankingCalculationService;
import cn.iocoder.yudao.module.system.service.subjectcombination.SubjectCombinationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.stream.Collectors;

/**
 * 成绩导入性能优化服务
 * 主要优化点：
 * 1. 批量数据库操作，减少数据库交互次数
 * 2. 异步处理耗时操作
 * 3. 优化事务管理，减少长事务
 * 4. 缓存重复查询结果
 * 5. 分批处理大数据量
 *
 * @author 芋道源码
 */
@Service
@Slf4j
public class ExamScoreImportOptimizedService {

    @Resource
    private ExamScoreMapper examScoreMapper;
    
    @Resource
    private ExamClassMapper examClassMapper;
    
    @Resource
    private ExamClassStudentMapper examClassStudentMapper;
    
    @Resource
    private ExamScoreMapperBatch examScoreMapperBatch;
    
    @Resource
    private SubjectScoreDetailMapper subjectScoreDetailMapper;
    
    @Resource
    private TopScoreRecordMapper topScoreRecordMapper;
    
    @Resource
    private AutoGenerationService autoGenerationService;
    
    @Resource
    private SubjectCombinationService subjectCombinationService;
    
    @Resource
    private RankingCalculationService rankingCalculationService;
    
    @Resource
    private Executor taskExecutor;
    
    // 批处理大小配置
    private static final int BATCH_SIZE = 500;
    private static final int MAX_BATCH_SIZE = 1000;
    
    /**
     * 优化后的成绩导入方法
     * 主要优化：
     * 1. 分批处理数据
     * 2. 批量数据库操作
     * 3. 异步处理排名计算
     * 4. 缓存重复查询
     */
    @Transactional(rollbackFor = Exception.class)
    public ExamScoreImportRespVO importNewExamScoresOptimized(MultipartFile file) throws IOException {
        log.info("开始优化版成绩导入，文件名: {}", file.getOriginalFilename());
        long startTime = System.currentTimeMillis();
        
        try {
            // 1. 解析Excel文件（保持原有逻辑）
            List<NewExamScoreImportExcelVO> importDataList = parseExcelFile(file);
            log.info("解析Excel完成，共{}条数据", importDataList.size());
            
            // 2. 数据预处理和验证
            BatchProcessingContext context = preprocessData(importDataList);
            log.info("数据预处理完成，需要创建{}个班级，{}个学生", 
                    context.getNewClasses().size(), context.getNewStudents().size());
            
            // 3. 批量创建班级和学生（优化版）
            batchCreateClassesAndStudents(context);
            log.info("批量创建班级和学生完成");
            
            // 4. 分批处理成绩数据
            ExamScoreImportRespVO result = batchProcessScores(context);
            
            // 5. 异步处理排名计算（不阻塞主流程）
            if (context.getExamId() != null) {
                CompletableFuture.runAsync(() -> {
                    try {
                        log.info("开始异步计算排名，考试ID: {}", context.getExamId());
                        rankingCalculationService.calculateAllRankings(context.getExamId());
                        log.info("异步排名计算完成，考试ID: {}", context.getExamId());
                    } catch (Exception e) {
                        log.error("异步排名计算失败，考试ID: {}", context.getExamId(), e);
                    }
                }, taskExecutor);
            }
            
            long endTime = System.currentTimeMillis();
            log.info("优化版成绩导入完成，耗时: {}ms", endTime - startTime);
            
            return result;
            
        } catch (Exception e) {
            log.error("优化版成绩导入失败", e);
            throw e;
        }
    }
    
    /**
     * 数据预处理和验证
     * 构建批处理上下文，缓存重复查询结果
     */
    private BatchProcessingContext preprocessData(List<NewExamScoreImportExcelVO> importDataList) {
        BatchProcessingContext context = new BatchProcessingContext();
        
        // 提取所有班级名称
        Set<String> classNames = importDataList.stream()
                .map(NewExamScoreImportExcelVO::getClassName)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        
        // 批量查询现有班级（减少数据库查询次数）
        Map<String, ExamClassDO> existingClasses = batchQueryExistingClasses(classNames);
        context.setExistingClasses(existingClasses);
        
        // 识别需要创建的新班级
        Set<String> newClassNames = classNames.stream()
                .filter(name -> !existingClasses.containsKey(name))
                .collect(Collectors.toSet());
        context.setNewClasses(newClassNames);
        
        // 提取所有学生信息
        Set<String> studentKeys = importDataList.stream()
                .map(data -> data.getStudentName() + "#" + data.getClassName())
                .collect(Collectors.toSet());
        
        // 批量查询现有学生
        Map<String, ExamClassStudentDO> existingStudents = batchQueryExistingStudents(studentKeys);
        context.setExistingStudents(existingStudents);
        
        // 识别需要创建的新学生
        Set<NewExamScoreImportExcelVO> newStudents = importDataList.stream()
                .filter(data -> {
                    String key = data.getStudentName() + "#" + data.getClassName();
                    return !existingStudents.containsKey(key);
                })
                .collect(Collectors.toSet());
        context.setNewStudents(newStudents);
        
        // 提取选科组合
        Set<String> subjectCombinations = importDataList.stream()
                .map(NewExamScoreImportExcelVO::getSubjectCombination)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        context.setSubjectCombinations(subjectCombinations);
        
        context.setImportDataList(importDataList);
        
        return context;
    }
    
    /**
     * 批量查询现有班级
     */
    private Map<String, ExamClassDO> batchQueryExistingClasses(Set<String> classNames) {
        if (classNames.isEmpty()) {
            return new HashMap<>();
        }
        
        List<ExamClassDO> existingClasses = examScoreMapperBatch.selectBatchByClassNames(new ArrayList<>(classNames));
        return existingClasses.stream()
                .collect(Collectors.toMap(ExamClassDO::getClassName, clazz -> clazz));
    }
    
    /**
     * 批量查询现有学生
     */
    private Map<String, ExamClassStudentDO> batchQueryExistingStudents(Set<String> studentKeys) {
        if (studentKeys.isEmpty()) {
            return new HashMap<>();
        }
        
        Map<String, ExamClassStudentDO> result = new HashMap<>();
        
        // 按班级分组查询学生
        Map<String, List<String>> studentsByClass = studentKeys.stream()
                .collect(Collectors.groupingBy(
                    key -> key.split("#")[1], // 班级名称
                    Collectors.mapping(key -> key.split("#")[0], Collectors.toList()) // 学生姓名
                ));
        
        for (Map.Entry<String, List<String>> entry : studentsByClass.entrySet()) {
            String className = entry.getKey();
            List<String> studentNames = entry.getValue();
            
            List<ExamClassStudentDO> students = examScoreMapperBatch
                        .selectByClassNameAndStudentNames(className, studentNames);
            
            for (ExamClassStudentDO student : students) {
                String key = student.getStudentName() + "#" + student.getClassName();
                result.put(key, student);
            }
        }
        
        return result;
    }
    
    /**
     * 批量创建班级和学生
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchCreateClassesAndStudents(BatchProcessingContext context) {
        // 1. 批量创建班级
        if (!context.getNewClasses().isEmpty()) {
            List<ExamClassDO> classesToCreate = new ArrayList<>();
            
            for (String className : context.getNewClasses()) {
                ExamClassDO examClass = createClassEntity(className);
                classesToCreate.add(examClass);
            }
            
            // 批量插入班级
            examScoreMapperBatch.insertBatchClasses(classesToCreate);
            log.info("批量创建{}个班级完成", classesToCreate.size());
            
            // 更新上下文中的班级信息
            for (ExamClassDO clazz : classesToCreate) {
                context.getExistingClasses().put(clazz.getClassName(), clazz);
            }
        }
        
        // 2. 批量创建学生
        if (!context.getNewStudents().isEmpty()) {
            List<ExamClassStudentDO> studentsToCreate = new ArrayList<>();
            
            for (NewExamScoreImportExcelVO studentData : context.getNewStudents()) {
                ExamClassDO clazz = context.getExistingClasses().get(studentData.getClassName());
                if (clazz != null) {
                    ExamClassStudentDO student = createStudentEntity(studentData, clazz.getId());
                    studentsToCreate.add(student);
                }
            }
            
            // 批量插入学生
            if (!studentsToCreate.isEmpty()) {
                examScoreMapperBatch.insertBatchStudents(studentsToCreate);
                log.info("批量创建{}个学生完成", studentsToCreate.size());
                
                // 更新上下文中的学生信息
                for (ExamClassStudentDO student : studentsToCreate) {
                    String key = student.getStudentName() + "#" + student.getClassName();
                    context.getExistingStudents().put(key, student);
                }
            }
        }
        
        // 3. 批量创建选科组合
        for (String combination : context.getSubjectCombinations()) {
            subjectCombinationService.autoCreateSubjectCombination(combination);
        }
    }
    
    /**
     * 分批处理成绩数据
     */
    private ExamScoreImportRespVO batchProcessScores(BatchProcessingContext context) {
        List<NewExamScoreImportExcelVO> importDataList = context.getImportDataList();
        ExamScoreImportRespVO result = ExamScoreImportRespVO.builder()
                .createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>())
                .duplicateUsernames(new ArrayList<>())
                .failureUsernames(new HashMap<>())
                .build();
        
        // 分批处理
        int totalSize = importDataList.size();
        int batchSize = Math.min(BATCH_SIZE, MAX_BATCH_SIZE);
        
        for (int i = 0; i < totalSize; i += batchSize) {
            int endIndex = Math.min(i + batchSize, totalSize);
            List<NewExamScoreImportExcelVO> batch = importDataList.subList(i, endIndex);
            
            log.info("处理第{}批数据，范围: {}-{}", (i / batchSize + 1), i + 1, endIndex);
            
            try {
                ExamScoreImportRespVO batchResult = processBatchScores(batch, context);
                mergeResults(result, batchResult);
            } catch (Exception e) {
                log.error("处理第{}批数据失败", (i / batchSize + 1), e);
                // 记录失败的批次
                for (NewExamScoreImportExcelVO data : batch) {
                    result.getFailureUsernames().put(data.getStudentName(), 
                            "批处理失败: " + e.getMessage());
                }
            }
        }
        
        return result;
    }
    
    /**
     * 处理单批成绩数据
     */
    @Transactional(rollbackFor = Exception.class)
    public ExamScoreImportRespVO processBatchScores(List<NewExamScoreImportExcelVO> batch, 
                                                    BatchProcessingContext context) {
        ExamScoreImportRespVO result = ExamScoreImportRespVO.builder()
                .createUsernames(new ArrayList<>())
                .updateUsernames(new ArrayList<>())
                .duplicateUsernames(new ArrayList<>())
                .failureUsernames(new HashMap<>())
                .build();
        
        // 1. 准备批量插入的数据
        List<ExamScoreDO> scoresToInsert = new ArrayList<>();
        List<ExamScoreDO> scoresToUpdate = new ArrayList<>();
        List<SubjectScoreDetailDO> detailsToInsert = new ArrayList<>();
        List<TopScoreRecordDO> topScoresToInsert = new ArrayList<>();
        
        // 2. 处理每条数据
        for (NewExamScoreImportExcelVO data : batch) {
            try {
                // 获取学生信息
                String studentKey = data.getStudentName() + "#" + data.getClassName();
                ExamClassStudentDO student = context.getExistingStudents().get(studentKey);
                
                if (student == null) {
                    result.getFailureUsernames().put(data.getStudentName(), "学生信息不存在");
                    continue;
                }
                
                // 获取班级信息
                ExamClassDO clazz = context.getExistingClasses().get(data.getClassName());
                if (clazz == null) {
                    result.getFailureUsernames().put(data.getStudentName(), "班级信息不存在");
                    continue;
                }
                
                // 检查是否已存在成绩记录
                Long examId = context.getExamId();
                ExamScoreDO existingScore = examScoreMapper.selectByExamIdAndStudentId(examId, student.getStudentId());
                
                // 创建成绩记录
                ExamScoreDO scoreRecord = createScoreRecord(data, examId, student, clazz);
                
                if (existingScore != null) {
                    // 更新现有记录
                    scoreRecord.setId(existingScore.getId());
                    scoresToUpdate.add(scoreRecord);
                    result.getUpdateUsernames().add(data.getStudentName());
                } else {
                    // 新增记录
                    scoresToInsert.add(scoreRecord);
                    result.getCreateUsernames().add(data.getStudentName());
                }
                
                // 创建科目详情记录
                List<SubjectScoreDetailDO> details = createSubjectDetails(data, examId, student.getStudentId());
                detailsToInsert.addAll(details);
                
                // 创建最高分记录
                List<TopScoreRecordDO> topScores = createTopScoreRecords(data, examId, student.getStudentId());
                topScoresToInsert.addAll(topScores);
                
            } catch (Exception e) {
                log.error("处理学生{}数据失败", data.getStudentName(), e);
                result.getFailureUsernames().put(data.getStudentName(), e.getMessage());
            }
        }
        
        // 3. 批量执行数据库操作
        if (!scoresToInsert.isEmpty()) {
            examScoreMapper.insertBatch(scoresToInsert);
            log.info("批量插入{}条成绩记录", scoresToInsert.size());
        }
        
        if (!scoresToUpdate.isEmpty()) {
            examScoreMapper.updateBatch(scoresToUpdate);
            log.info("批量更新{}条成绩记录", scoresToUpdate.size());
        }
        
        if (!detailsToInsert.isEmpty()) {
            subjectScoreDetailMapper.insertBatch(detailsToInsert);
            log.info("批量插入{}条科目详情记录", detailsToInsert.size());
        }
        
        if (!topScoresToInsert.isEmpty()) {
            topScoreRecordMapper.insertBatch(topScoresToInsert);
            log.info("批量插入{}条最高分记录", topScoresToInsert.size());
        }
        
        return result;
    }
    
    // ==================== 辅助方法 ====================
    
    private List<NewExamScoreImportExcelVO> parseExcelFile(MultipartFile file) throws IOException {
        // 这里保持原有的Excel解析逻辑
        // 为了简化示例，这里返回空列表
        return new ArrayList<>();
    }
    
    private ExamClassDO createClassEntity(String className) {
        // 创建班级实体的逻辑
        ExamClassDO clazz = new ExamClassDO();
        clazz.setClassName(className);
        
        // 设置当前登录用户的部门ID（用于数据权限控制）
        Long currentUserDeptId = SecurityFrameworkUtils.getLoginUserDeptId();
        if (currentUserDeptId != null) {
            clazz.setDeptId(currentUserDeptId);
            log.debug("设置班级部门ID: {}, 班级: {}", currentUserDeptId, className);
        } else {
            log.warn("当前登录用户未设置部门ID，班级将不设置部门信息: {}", className);
        }
        
        // 设置其他必要字段
        return clazz;
    }
    
    private ExamClassStudentDO createStudentEntity(NewExamScoreImportExcelVO data, Long classId) {
        // 创建学生实体的逻辑
        ExamClassStudentDO student = new ExamClassStudentDO();
        student.setStudentName(data.getStudentName());
        student.setClassName(data.getClassName());
        student.setClassId(classId);
        // 设置其他必要字段
        return student;
    }
    
    private ExamScoreDO createScoreRecord(NewExamScoreImportExcelVO data, Long examId, 
                                         ExamClassStudentDO student, ExamClassDO clazz) {
        // 创建成绩记录的逻辑
        ExamScoreDO score = new ExamScoreDO();
        score.setExamId(examId);
        score.setStudentId(student.getStudentId());
        score.setClassId(clazz.getId());
        
        // 设置当前登录用户的部门ID（用于数据权限控制）
        Long currentUserDeptId = SecurityFrameworkUtils.getLoginUserDeptId();
        if (currentUserDeptId != null) {
            score.setDeptId(currentUserDeptId);
            log.debug("批量导入成绩记录设置部门ID: {}, 班级: {}, 学生: {}", 
                     currentUserDeptId, clazz.getClassName(), data.getStudentName());
        } else {
            log.warn("当前登录用户未设置部门ID，成绩记录将不设置部门信息");
        }
        
        score.setTotalScore(data.getTotalOriginalScore());
        // 设置其他字段
        return score;
    }
    
    private List<SubjectScoreDetailDO> createSubjectDetails(NewExamScoreImportExcelVO data, 
                                                           Long examId, Long studentId) {
        // 创建科目详情记录的逻辑
        return new ArrayList<>();
    }
    
    private List<TopScoreRecordDO> createTopScoreRecords(NewExamScoreImportExcelVO data, 
                                                        Long examId, Long studentId) {
        // 创建最高分记录的逻辑
        return new ArrayList<>();
    }
    
    private void mergeResults(ExamScoreImportRespVO target, ExamScoreImportRespVO source) {
        target.getCreateUsernames().addAll(source.getCreateUsernames());
        target.getUpdateUsernames().addAll(source.getUpdateUsernames());
        target.getFailureUsernames().putAll(source.getFailureUsernames());
    }
    
    /**
     * 批处理上下文
     */
    public static class BatchProcessingContext {
        private Long examId;
        private List<NewExamScoreImportExcelVO> importDataList;
        private Map<String, ExamClassDO> existingClasses = new HashMap<>();
        private Map<String, ExamClassStudentDO> existingStudents = new HashMap<>();
        private Set<String> newClasses = new HashSet<>();
        private Set<NewExamScoreImportExcelVO> newStudents = new HashSet<>();
        private Set<String> subjectCombinations = new HashSet<>();
        
        // Getters and Setters
        public Long getExamId() { return examId; }
        public void setExamId(Long examId) { this.examId = examId; }
        
        public List<NewExamScoreImportExcelVO> getImportDataList() { return importDataList; }
        public void setImportDataList(List<NewExamScoreImportExcelVO> importDataList) { this.importDataList = importDataList; }
        
        public Map<String, ExamClassDO> getExistingClasses() { return existingClasses; }
        public void setExistingClasses(Map<String, ExamClassDO> existingClasses) { this.existingClasses = existingClasses; }
        
        public Map<String, ExamClassStudentDO> getExistingStudents() { return existingStudents; }
        public void setExistingStudents(Map<String, ExamClassStudentDO> existingStudents) { this.existingStudents = existingStudents; }
        
        public Set<String> getNewClasses() { return newClasses; }
        public void setNewClasses(Set<String> newClasses) { this.newClasses = newClasses; }
        
        public Set<NewExamScoreImportExcelVO> getNewStudents() { return newStudents; }
        public void setNewStudents(Set<NewExamScoreImportExcelVO> newStudents) { this.newStudents = newStudents; }
        
        public Set<String> getSubjectCombinations() { return subjectCombinations; }
        public void setSubjectCombinations(Set<String> subjectCombinations) { this.subjectCombinations = subjectCombinations; }
    }
}